//
//  ViewController.m
//  ProcessMemoryTest
//
//  Created by LiuLichen on 2023/6/12.
//

#import "ViewController.h"

#import <sys/resource.h>
#import <sys/types.h>
#import <sys/sysctl.h>
#import <sys/resource.h>

#import <Cocoa/Cocoa.h>
#import <unistd.h>

#include <spawn.h>
#include <sys/wait.h>


@implementation ViewController

- (double)getMemoryUsageWithPid:(pid_t)pid {
    double memoryUsageMB = 0;
    // 定义 sysctl 请求结构体
    struct kinfo_proc info;
    size_t info_size = sizeof(info);
    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
//    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, pid };

    // 获取进程信息
    if (sysctl(mib, 4, &info, &info_size, NULL, 0) == 0) {
        mach_port_t targetTask;
        kern_return_t kr = task_for_pid(mach_task_self(), pid, &targetTask);
        if (kr != KERN_SUCCESS) {
            NSLog(@"Failed to get task info for process with PID: %d", pid);
            return 0;
        }

        // 获取进程的物理内存大小
        struct task_basic_info taskInfo;
        mach_msg_type_number_t taskInfoCount = TASK_BASIC_INFO_COUNT;
        if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&taskInfo, &taskInfoCount) == KERN_SUCCESS) {
            // 转换为 MB
            memoryUsageMB = taskInfo.resident_size / (1024.0 * 1024.0);
            NSLog(@"进程的内存使用情况: %.2f MB", memoryUsageMB);
        }
    } else {
        NSLog(@"无法获取进程信息");
    }

    return memoryUsageMB;
}


- (double)getMemoryUsageWithPid2:(pid_t)pid {
    mach_port_t targetTask;
    kern_return_t kr = task_for_pid(mach_task_self(), pid, &targetTask);
    if (kr != KERN_SUCCESS) {
        NSLog(@"Failed to get task info for process with PID: %d", pid);
        return 0;
    }

    struct task_basic_info targetTaskInfo;
    mach_msg_type_number_t infoCount = TASK_BASIC_INFO_COUNT;
    kr = task_info(targetTask, TASK_BASIC_INFO, (task_info_t)&targetTaskInfo, &infoCount);
    if (kr != KERN_SUCCESS) {
        NSLog(@"Failed to get basic task info for process with PID: %d", pid);
        return 0;
    }

    unsigned long long memorySize = (unsigned long long)targetTaskInfo.resident_size;
    NSLog(@"Memory used by process with PID %d: %llu bytes", pid, memorySize);

    mach_port_deallocate(mach_task_self(), targetTask);
    return memorySize;
}

- (void)getSelfMemory {
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vmInfo, &count);
    if (kr == KERN_SUCCESS) {
        unsigned long long memorySize = (unsigned long long)vmInfo.phys_footprint;
        NSLog(@"Memory used: %llu bytes", memorySize);
    } else {
        NSLog(@"Failed to get memory information");
    }
}

- (void)launchChildProcess {
    NSURL *executableURL = [NSURL fileURLWithPath:@"/Users/lichenliu/Documents/study/mac_study/ios_study/ProcessMemoryTest/ProcessMemoryTest/PureApp.app"]; // 替换为你的可执行文件的路径

    NSError *error = nil;
    NSTask *task = [NSTask launchedTaskWithExecutableURL:executableURL arguments:@[] error:&error terminationHandler:^(NSTask *task) {
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    }];


    if (task) {
        pid_t pid = [task processIdentifier];
        NSLog(@"子进程已启动，进程ID：%d", pid);

        [task waitUntilExit];
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    } else {
        NSLog(@"启动子进程失败，错误：%@", error);
    }
}

- (void)launchChildProcess2 {
    pid_t pid = fork();

    if (pid == 0) {
        // 子进程代码
//        chdir([appPath UTF8String]); // 切换工作目录到 .app 的路径

        // 执行 .app 内的可执行文件
        NSString *appPath = [[NSBundle mainBundle] pathForResource:@"PureApp" ofType:@"app"];
        const char *executable = [[appPath stringByAppendingPathComponent:@"Contents/MacOS/PureApp"] UTF8String];
        const char *arguments[] = {executable, NULL};
        execvp(executable, (char * const *)arguments);

        // 如果 execvp 执行成功，以下代码不会执行
        NSLog(@"执行失败");
        exit(1);
    } else if (pid > 0) {
        // 父进程代码
        NSLog(@"已启动子进程");
    } else {
        // fork 失败
        NSLog(@"fork 失败");
    }

    // 等待子进程退出
    int status;
    wait(&status);
}

- (void)launchChildProcess3 {
    NSString *appPath = [[NSBundle mainBundle] pathForResource:@"PureApp" ofType:@"app"];
    if (appPath) {
        NSTask *task = [[NSTask alloc] init];
        task.launchPath = @"/usr/bin/open";
        task.arguments = @[appPath];
        [task launch];
        [task waitUntilExit];
    }
}

- (void)launchChildProcess4 {
    NSString *childAppPath = [[NSBundle mainBundle] pathForResource:@"ChildApp" ofType:@"app"];
    NSURL *childAppURL = [NSURL fileURLWithPath:childAppPath];
    NSError *error = nil;
    NSTask *task = [NSTask launchedTaskWithExecutableURL:childAppURL arguments:@[] error:&error terminationHandler:^(NSTask *task) {
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    }];


    if (task) {
        pid_t pid = [task processIdentifier];
        NSLog(@"子进程已启动，进程ID：%d", pid);

        [task waitUntilExit];
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    } else {
        NSLog(@"启动子进程失败，错误：%@", error);
    }
}

- (void)launchChildProcess5 {
    NSString *appPath = [[NSBundle mainBundle] pathForResource:@"PureApp" ofType:@"app"];
//    NSString* exePath = [NSString stringWithFormat:@"%@/Contents/MacOS/Pure", appPath];
    const char* cAppPath = [appPath cStringUsingEncoding:NSUTF8StringEncoding];
    pid_t pid;
    char *const argv[] = {"PureApp", NULL};
    char *const envp[] = {NULL};

    posix_spawnattr_t attr;
    posix_spawn_file_actions_t file_actions;

    posix_spawnattr_init(&attr);
    posix_spawn_file_actions_init(&file_actions);

    int result = posix_spawnp(&pid, cAppPath, &file_actions, &attr, argv, envp);

    if (result == 0) {
        // 子进程创建成功
        int status;
        waitpid(pid, &status, 0);
    } else {
        // 子进程创建失败
        fprintf(stderr, "posix_spawnp failed: %s\n", strerror(result));
    }
}

- (void)launchChildProcess6 {
    NSURL* childURL = [[NSBundle mainBundle] URLForAuxiliaryExecutable:@"ChildCommandLineTool"];
    NSError *error = nil;
    NSTask *task = [NSTask launchedTaskWithExecutableURL:childURL arguments:@[] error:&error terminationHandler:^(NSTask *task) {
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    }];


    if (task) {
        pid_t pid = [task processIdentifier];
        NSLog(@"子进程已启动，进程ID：%d", pid);
//        double memoryUsage = [self getMemoryUsageWithPid:pid];
        double memoryUsage = [self getMemoryUsageWithPid2:pid];
        NSLog(@"Memory Usage: %f", memoryUsage);

        [task waitUntilExit];
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    } else {
        NSLog(@"启动子进程失败，错误：%@", error);
    }
}

- (void)launchChildProcess7 {
    NSString* childAppPath = [[NSBundle mainBundle] pathForAuxiliaryExecutable:@"ChildApp.app"];
    NSString* exePath = [NSString stringWithFormat:@"%@/Contents/MacOS/ChildApp", childAppPath];
    NSError *error = nil;
    NSTask *task = [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:exePath] arguments:@[] error:&error terminationHandler:^(NSTask *task) {
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    }];


    if (task) {
        pid_t pid = [task processIdentifier];
        NSLog(@"子进程已启动，进程ID：%d", pid);
//        double memoryUsage = [self getMemoryUsageWithPid:pid];
        double memoryUsage = [self getMemoryUsageWithPid2:pid];
        NSLog(@"Memory Usage: %f", memoryUsage);

        [task waitUntilExit];
        NSInteger terminationStatus = [task terminationStatus];
        NSLog(@"子进程已退出，退出状态码：%ld", (long)terminationStatus);
    } else {
        NSLog(@"启动子进程失败，错误：%@", error);
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    [self getSelfMemory];

//    NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
//    NSArray<NSRunningApplication *> *runningApplications = [workspace runningApplications];

//    for (NSRunningApplication *application in runningApplications) {
//        pid_t pid = [application processIdentifier];
////        double memoryUsage = [self getMemoryUsageWithPid:pid];
////        double memoryUsage = memoryUsageForProcess(pid);
////        NSLog(@"Process ID: %d, memory usage: %f", pid, memoryUsage);
//    }
//    if (@available(macOS 10.15, *)) {
//      [[NSWorkspace sharedWorkspace]
//          openApplicationAtURL:[NSURL fileURLWithPath:@"/Users/lichenliu/Documents/study/mac_study/ios_study/ProcessMemoryTest/ProcessMemoryTest/PureApp.app"]
//                 configuration:[NSWorkspaceOpenConfiguration new]
//             completionHandler:^(NSRunningApplication* app, NSError* error) {
//               if (error) {
//                 NSLog(@"Failed to run the app: %@",
//                       error.localizedDescription);
//               } else {
//                 pid_t pid = app.processIdentifier;
////                   [self getMemoryUsageWithPid:pid];
//                   double memoryUsage = memoryUsageForProcess(pid);
//                   NSLog(@"Process ID: %d, memory usage: %f", pid, memoryUsage);
//               }
//             }];
//    }

//    [self launchChildProcess];
//    [self launchChildProcess2];
//    [self launchChildProcess3];
//    [self launchChildProcess4];
//    [self launchChildProcess5];
//    [self launchChildProcess6];
    [self launchChildProcess7];
}


- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];

    // Update the view, if already loaded.
}


@end
